package novel.manager.service.impl.storage;

import novel.manager.pojo.spider.NovelSiteEnum;
import novel.manager.pojo.spider.ThreadConfiguration;
import novel.manager.pojo.web.Novel;
import novel.manager.service.iface.spider.download.INovelDownload;
import novel.manager.service.iface.spider.novel.INovelSpider;
import novel.manager.service.iface.storage.IProcessorStorage;
import novel.manager.service.impl.spider.download.NovelDownload;
import novel.manager.service.utils.NovelSpiderFactory;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
//抽象类实现处理器接口
public abstract class AbstractNovelStorage implements IProcessorStorage {
	//读取mybatis配置文件，创建SqlSessionFactory用来创建SqlSession来执行mysql语句
	protected SqlSessionFactory sqlSessionFactory;
	//创建任务映射
	protected Map<String, String> tasks = new TreeMap<>();
	//无参构造器:子类创建对象调用无参构造方法时会先调用父类的无参构造方法再调用子类的无惨构造方法
	public AbstractNovelStorage() throws Exception {
		/*URL url = this.getClass().getClassLoader().getResource("mybatis/SqlMapConfig.xml");
		System.out.println("url:"+url);
		//睡一会观察路径是否读取成功
		Thread.sleep(3_000);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream(url.getPath()));*/
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis/SqlMapConfig.xml"));

	}
	//覆盖处理器方法
	@Override
	public void processRun() {
		//创建指定线程数的线程池
		ExecutorService service = Executors.newFixedThreadPool(tasks.size());
		//期货订单集合
		List<Future<String>> futures = new ArrayList<>(tasks.size());
		//遍历线程任务的分配并下单
		for (Entry<String, String> entry : tasks.entrySet()) {
			//匿名内部类要使用其外的局部变量时需要在局部变量添加final
			final String key = entry.getKey();
			final String value = entry.getValue();
			//使用匿名内部类的方式：提交任务给线程池执行任务:期货下单操作，并添加到期货订单集合
			futures.add(service.submit(new Callable<String> () {
				@Override
				/**把测试文件中的测试抓取笔下文学的小说列表和翻页功能写在这里：线程运行*/
				public String call() throws Exception {
					//接口代理抓取功能的实现类
					INovelSpider spider = NovelSpiderFactory.getNovelSpider(value);
					//抓取功能的实现类调用迭代方法返回专门用于对小说网站小说列表抓取的迭代器
					Iterator<List<Novel>> iterator = spider.iterator(value, 3);
					//记录页码
					int page=0;
					while (iterator.hasNext()) {
						page++;
						//抽象类实现接口，抽象类不要求覆盖接口方法，但抽象类+抽象类的子非抽象类要覆盖接口的全部方法
						//抽象类调用抽象类的子非抽象类的方法
						//执行小说列表的爬取
						List<Novel> novels = iterator.next();
						//记录当前页的第几本
						int count=0;
						//遍历小说列表并设置小说名字的首字母
						for (Novel novel : novels) {
							count++;
							//设置小说的名字的首字母
							novel.setFirstLetter(key.charAt(0));
							//不用再设置小说保存时间，因为已经在数据库生成时定义为now
							//-------------------------------------
							INovelDownload download = new NovelDownload(); 
							ThreadConfiguration threadConfiguration = new ThreadConfiguration();
							//下载保存路径
							threadConfiguration.setPath("C:/Users/asjus/Desktop/download/"+  NovelSiteEnum.getEnumByUrl(value).getUrl()+"/第"+key+"类/第"+page+"页/第"+count+"本_"+novel.getName());
							//url：章节列表,进行下载
							System.out.println("第"+key+"类/第"+page+"页/第"+count+"本:"+novel.getName()+"，小说，下载好了，文件保存在：" + download.download(novel.getUrl(), threadConfiguration) + "这里，赶紧去看看吧！");
						}
						//打印下一页小说列表的地址：err是显示红色的，用来打印错误，用于普通打印时其凸显作用
						if(spider.next().equals("")||spider.next().trim()==null){
							System.err.println("已经完成测试抓取小说列表和翻页功能");				
						}else{				
							System.err.println("下一页小说列表的地址URL：" + spider.next());
						}
						SqlSession session = sqlSessionFactory.openSession();
						//批量保存小说列表到数据库
						session.insert("batchInsert", novels);
						session.commit();
						session.close();
						//睡一会，过于频繁的话会被封ip
						Thread.sleep(1_000);
					}
					//返回任意String:线程的任务完成的标志
					return key;
				}				
			}));
		}				
		//不能再添加新线程，等任务结束后关闭线程池
		service.shutdown();
		//遍历期货订单集合进行交割提货future.get()
		for (Future<String> future : futures) {
			try {
				//future.get()：Waits if necessary for the computation to complete, and then retrieves its result.
				//等待线程任务完成，打印完成下载的文件路径和线程任务完成的标志
				System.out.println("抓取[" + future.get() + "]结束！");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
